package scorer;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.border.EtchedBorder;

import org.python.core.Py;
import org.python.core.PySystemState;

import scorer.Set.TYPE;
import tile.Tile;

class ScorerFrame extends JFrame {

	private static final long serialVersionUID = 1L;

	public ScorerFrame() {
		setTitle("MahJong Scorer");
		setSize(1005, 850);
		Container content = getContentPane();

		tiles = new HashMap<String, BufferedImage>();

		all = new ArrayList<String>();
		try {
			ArrayList<Tile> temp = new ArrayList<Tile>();
			ScorerMain.generateTiles(temp, 4);
			for (Tile tile : temp) {
				all.add(tile.toString());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		try {
			createImages();
		} catch (Exception e) {
			e.printStackTrace();
		}

		setsPanel = new JPanel(new GridLayout(6, 1));
		setsPanel.setBorder(BorderFactory
				.createEtchedBorder(EtchedBorder.RAISED));
		setsPanel.setMinimumSize(new Dimension(200, 400));
		setsPanel.setPreferredSize(new Dimension(300, 400));
		setsPanel.setMaximumSize(new Dimension(400, 800));

		setsPanel.add(new JLabel("Current sets:"));

		currentSet = -1;
		sets = new SetPanel[5];

		JPanel main = new JPanel(new BorderLayout());
		main.setBorder(BorderFactory.createLineBorder(Color.black, 3));
		// main.setLayout(null);

		choices = new JPanel();
		choices.setLayout(new BoxLayout(choices,BoxLayout.PAGE_AXIS));
		choices.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));

		choices.setMinimumSize(new Dimension(200, 400));
		choices.setPreferredSize(new Dimension(700, 400));
		choices.setMaximumSize(new Dimension(800, 800));

		// choices;

		choices.add(Box.createVerticalStrut(15));
		choices.add(createChooseSets());
		choices.add(Box.createVerticalGlue());
		choices.add(createChooseTiles());
		choices.add(Box.createVerticalGlue());
		choices.add(createChooseMisc());

		setComponentEnabled(chooseSet, true);
		setComponentEnabled(chooseTiles, false);
		setComponentEnabled(chooseMisc, false);

		main.add(setsPanel, BorderLayout.LINE_START);

		main.add(choices, BorderLayout.LINE_END);

		content.add(main, BorderLayout.CENTER);

		try {
			engine = makeEngine();
		} catch (ScriptException e1) {
			e1.printStackTrace();
		}

	}

	ArrayList<String> all;
	ArrayList<JButton> tileButtons;
	Tile winner;
	int currentSet;
	JPanel chooseSet, chooseTiles, chooseMisc;
	JPanel setsPanel;
	JPanel choices;
	SetPanel[] sets;
	JComboBox seat;
	JComboBox prevailing;
	JCheckBox kong, rob, last_turn, self_draw;
	JSpinner flowers;
	JLabel score;
	JTextArea fanNames;
	HashMap<String, BufferedImage> tiles;

	public JPanel createChooseMisc() {
		chooseMisc = new JPanel();
		chooseMisc.setLayout(new BoxLayout(chooseMisc, BoxLayout.PAGE_AXIS));

		JPanel misc = new JPanel(new GridLayout(5, 2));
		misc.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));

		seat = new JComboBox(new String[] { "East", "North", "West", "South" });
		prevailing = new JComboBox(new String[] { "East", "North", "West",
				"South" });

		kong = new JCheckBox("Replacement tile win");
		rob = new JCheckBox("Robbed the kong");
		last_turn = new JCheckBox("Last turn");
		self_draw = new JCheckBox("Self-draw");

		flowers = new JSpinner(new SpinnerNumberModel(0, 0, 8, 1));

		misc.add(new JLabel("Seat Wind:"));
		misc.add(seat);
		misc.add(new JLabel("Prevailing Wind:"));
		misc.add(prevailing);
		misc.add(kong);
		misc.add(rob);
		misc.add(last_turn);
		misc.add(self_draw);
		misc.add(new JLabel("Number of Flowers:"));
		misc.add(flowers);

		JButton clear = new JButton("Clear hand");
		clear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				all = new ArrayList<String>();
				try {
					ArrayList<Tile> temp = new ArrayList<Tile>();
					ScorerMain.generateTiles(temp, 4);
					for (Tile tile : temp) {
						all.add(tile.toString());
					}
				} catch (Exception exc) {
					exc.printStackTrace();
				}
				for (JButton button : tileButtons) {
					button.setEnabled(true);
					button.setVisible(true);
				}
				score.setText("Score: ");
				kong.setSelected(false);
				rob.setSelected(false);
				last_turn.setSelected(false);
				self_draw.setSelected(false);
				fanNames.setText("");
				setsPanel.removeAll();
				setsPanel.add(new JLabel("Current Sets:"));
				flowers.setValue(0);
				currentSet = -1;
				setComponentEnabled(chooseSet, true);
				setComponentEnabled(chooseTiles, false);
				setComponentEnabled(chooseMisc, false);
				validate();
				repaint();
			}
		});

		JButton scoreButton = new JButton("Score hand");
		scoreButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					String s = getHandAsString();
					boolean ok = s.matches(".* w .. .*");
					if (ok) {
						String hand_score = checkFan(s) + "";
						if (!hand_score.equals("0"))
							score.setText("Score: " + hand_score);
						else {
							score.setText("Not a mahjong!");
						}
					} else {
						fanNames.setText("Error: No winning tile selected!");
					}
				} catch (ScriptException e1) {
					e1.printStackTrace();
				}

			}
		});

		score = new JLabel("Score: ");
		score.setHorizontalAlignment(SwingConstants.CENTER);
		score.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));

		fanNames = new JTextArea();
		JScrollPane editorScrollPane = new JScrollPane(fanNames);
		editorScrollPane
				.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		editorScrollPane.setPreferredSize(new Dimension(250, 100));
		editorScrollPane.setMinimumSize(new Dimension(10, 10));

		JPanel buttonPanel = new JPanel(new GridLayout(1, 4));
		buttonPanel.setBorder(BorderFactory
				.createEtchedBorder(EtchedBorder.RAISED));
		buttonPanel.add(clear);
		buttonPanel.add(scoreButton);
		buttonPanel.add(score);

		chooseMisc.add(misc);
		chooseMisc.add(buttonPanel);
		chooseMisc.add(editorScrollPane);

		return chooseMisc;
	}

	public JPanel createChooseSets() {
		chooseSet = new JPanel();
		chooseSet.setLayout(new GridLayout(2, TYPE.values().length));
		chooseSet.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));

		ArrayList<Component> stuff1 = new ArrayList<Component>();
		ArrayList<Component> stuff2 = new ArrayList<Component>();

		JButton button;
		for (TYPE t : TYPE.values()) {
			button = new JButton("Closed " + t);
			button.addActionListener(new SetChooserActionListener());
			// System.out.println("we added open button? :"+t+":");
			stuff1.add(button);
			if (t.openAllowed) {
				button = new JButton("Open " + t);
				button.addActionListener(new SetChooserActionListener());
				stuff2.add(button);
			} else {
				stuff2.add(new JPanel());
			}
		}

//		for (int i = 0; i < 10; i++) {
//			chooseSet.add(new JPanel());
//		}

		for (Component but : stuff1) {
			chooseSet.add(but);
		}
		for (Component but : stuff2) {
			chooseSet.add(but);
		}

//		for (int i = 0; i < 10; i++) {
//			chooseSet.add(new JPanel());
//		}

		((GridLayout) chooseSet.getLayout()).setHgap(30);
		((GridLayout) chooseSet.getLayout()).setVgap(10);

		return chooseSet;
	}

	public JPanel createChooseTiles() {
		chooseTiles = new JPanel();
		chooseTiles.setBorder(BorderFactory.createEtchedBorder());
		chooseTiles.setPreferredSize(new Dimension(300,255));

		tileButtons = new ArrayList<JButton>();

		ArrayList<Tile> temp = new ArrayList<Tile>();
		try {
			ScorerMain.generateTiles(temp, 1);
			Collections.sort(temp);

			JButton button;
			for (Tile t : temp) {
				button = new JButton(new ImageIcon(tiles.get(t.toString())));
				tileButtons.add(button);
				button.setActionCommand(t.toString());
				button.addActionListener(new TileChooserActionListener());
				chooseTiles.add(button);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return chooseTiles;
	}

	class SetChooserActionListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			currentSet++;
			String command = e.getActionCommand();
			System.out.println(command);
			String[] parsed = command.split(" ");
			sets[currentSet] = new SetPanel(parsed[0].equalsIgnoreCase("open"),
					TYPE.valueOf(parsed[1]));
			sets[currentSet].addMouseListener(new TileMouseListener());
			setsPanel.add(sets[currentSet]);
			setsPanel.validate();
			setsPanel.repaint();
			setComponentEnabled(chooseTiles, true);
			setComponentEnabled(chooseSet, false);
		}
	}

	class TileChooserActionListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			String command = e.getActionCommand();
			System.out.println(command);
			boolean added = sets[currentSet].addToSet(new Tile(command));
			if (added) {
				all.remove(command);
				if (!all.contains(command)) {
					((JButton) e.getSource()).setEnabled(false);
					((JButton) e.getSource()).setVisible(false);
				}
				if (sets[currentSet].finishedSet()) {
					if (sets[currentSet].isOther() || currentSet + 1 == 5) {
						setComponentEnabled(chooseMisc, true);
						setComponentEnabled(chooseTiles, false);
					} else {
						setComponentEnabled(chooseSet, true);
						setComponentEnabled(chooseTiles, false);
					}
				}
			}
			System.out.println("We added the tile successfully: " + added);
		}
	}

	class TileMouseListener implements MouseListener {

		@Override
		public void mouseClicked(MouseEvent e) {
			int x = e.getX(), y = e.getY();
			if (y >= SetPanel.yoffset && y <= SetPanel.yoffset + SetPanel.width) {
				int index = (x - SetPanel.xoffset)
						/ (SetPanel.width + SetPanel.hgap);
				Tile t = ((SetPanel) e.getSource()).getTile(index);
				if (t != null) {
					System.out.println("Setting tile " + t + " as winner");
					t.isWinner = true;
					if (winner != null) {
						winner.isWinner = false;
					}
					winner = t;
				}
				repaint();
			} else if (SetPanel.exit.contains(x, y)) {
				int index = -1;
				for (int i = 0; i < sets.length; i++) {
					if (sets[i] != null && sets[i].equals(e.getSource())) {
						index = i;
					}
				}
				if (index != -1) {
					setsPanel.remove(sets[index]);
					Tile t = sets[index].removeFromSet(0);
					while (t != null) {
						all.add(t.toString());
						for (JButton button : tileButtons) {
							if (button.getActionCommand().equals(t.toString())) {
								button.setEnabled(true);
								button.setVisible(true);
							}
						}
						t = sets[index].removeFromSet(0);
					}
					sets[index] = null;
					for (int i = index + 1; i < sets.length; i++) {
						if (sets[i] != null) {
							sets[i - 1] = sets[i];
						}
					}
					
					boolean misc = false;
					
					for(Component c:choices.getComponents()) {
						if(c.isVisible() && c.equals(chooseMisc))
							misc = true;
					}
					
					if (currentSet == 0 || currentSet == index || misc) {
						setComponentEnabled(chooseSet, true);
						setComponentEnabled(chooseTiles, false);
						setComponentEnabled(chooseMisc, false);
					}
					currentSet--;
					validate();
					repaint();
				}
			}
		}

		@Override
		public void mouseEntered(MouseEvent e) {
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
		}

		@Override
		public void mousePressed(MouseEvent arg0) {
		}

		@Override
		public void mouseReleased(MouseEvent arg0) {
		}

	}

	public void setComponentEnabled(JComponent comp,boolean enabled) {
		for(Component c:comp.getComponents()) {
			if(c instanceof JComponent)
				setComponentEnabled((JComponent)c, enabled);
			else
				c.setEnabled(enabled);
		}
		comp.setEnabled(enabled);
	}
	
	public void createImages() throws Exception {
		ArrayList<Tile> temp = new ArrayList<Tile>();
		ScorerMain.generateTiles(temp, 1);
		Collections.sort(temp);
		for (Tile t : temp) {
			XpmParser parser = new XpmParser(getClass().getResourceAsStream(
					"tiles-numbered/" + t.getValue().toString().substring(0, 1)
							+ t.getClass().getSimpleName().substring(0, 1)
							+ ".xpm"));
			if (!parser.parse())
				throw new Exception("Error: Failed to parse file.");
			// System.out
			// .println(parser.getWidth() + " " + parser.getHeight());
			// for(Color c:parser.getColorTable()) {
			// System.out.println(c);
			// }
			// int count = 0;
			// for (byte b : parser.getPixmap()) {
			// System.out.print(b);
			// count++;
			// if (count % parser.getWidth() == 0)
			// System.out.println("");
			// }
			tiles.put(t.toString(), drawXPM(parser));
		}

		XpmParser parser = new XpmParser(getClass().getResourceAsStream(
				"close_button.xpm"));
		if (!parser.parse())
			throw new Exception("Error: Failed to parse close_button.xpm");
		tiles.put("close_button", drawXPM(parser));
		SetPanel.images = tiles;
	}

	public BufferedImage drawXPM(XpmParser parser) {
		// create color map
		Color[] colorTable = parser.getColorTable();

		BufferedImage img = new BufferedImage(parser.getWidth(),
				parser.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
		byte[] pix = parser.getPixmap();
		int w = parser.getWidth(), h = parser.getHeight();
		for (int i = 0; i < h; i++) {
			for (int j = 0; j < w; j++) {
				img.setRGB(j, i, colorTable[pix[j + w * i]].getRGB());
			}

		}
		BufferedImage scaledImage = new BufferedImage(w * 3 / 2, h * 3 / 2,
				BufferedImage.TYPE_INT_ARGB);
		Graphics2D graphics2D = scaledImage.createGraphics();
		AffineTransform xform = AffineTransform.getScaleInstance(1.5, 1.5);
		graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		graphics2D.drawImage(img, xform, null);
		graphics2D.dispose();
		return scaledImage;

	}

	public int checkFan(String s) throws ScriptException {
		String usedfans = "";
		int score = 0;
		engine.eval("m = find_mahjong('" + s + "')");

		// extract the is_mahjong
		System.out.println(engine.get("m"));
		engine.eval("is_mahjong = m['is_mahjong']");
		boolean isMahjong = getAsBoolean("is_mahjong");

		if (isMahjong) {
			// extract the score
			engine.eval("score = m['score']");

			score = getAsInt("score");
			for (Fan fan : extractFan()) {
				usedfans += fan.toString() + "\n";
			}
		}
		fanNames.setText(usedfans);
		return score;
	}

	private List<Fan> extractFan() throws ScriptException {
		// extract the nb of fans
		engine.eval("nbFans = len(m['fans'])");
		int nbFans = getAsInt("nbFans");

		// extract fans
		ArrayList<Fan> tempFans = new ArrayList<Fan>();
		for (int i = 0; i < nbFans; i++) {
			// extract points
			engine.eval("points = m['fans'][" + i + "]['points']");
			int points = getAsInt("points");
			// extract tiles
			engine.eval("tiles = m['fans'][" + i + "]['tiles']");
			// extract name
			engine.eval("name = m['fans'][" + i + "]['name']");
			String name = getAsString("name");
			// add to fan list
			tempFans.add(new Fan(points, name, getAsString("tiles")));
		}
		;
		return tempFans;
	}

	private int getAsInt(String pyVar) {
		return (Integer) engine.get(pyVar);
	}

	private String getAsString(String pyVar) {
		return (String) engine.get(pyVar);
	}

	private boolean getAsBoolean(String pyVar) {
		return (Boolean) engine.get(pyVar);
	}

	private String getHandAsString() {
		String h = "";

		for (SetPanel s : sets) {
			h += s.getSet();
		}

		if ((Integer) flowers.getValue() != 0) {
			h += " f ";
			int numFlowers = (Integer) flowers.getValue();
			for (int i = 1; i <= numFlowers; i++) {
				h += "F" + i;
			}
		}

		h += " sw W"
				+ seat.getSelectedItem().toString().substring(0, 1)
						.toLowerCase()
				+ " rw W"
				+ prevailing.getSelectedItem().toString().substring(0, 1)
						.toLowerCase() + " ";
		if (kong.isSelected()) {
			h += "kong_replacement ";
		}
		if (rob.isSelected()) {
			h += "robbing ";
		}
		if (last_turn.isSelected()) {
			h += "last_turn ";
		}
		if (self_draw.isSelected()) {
			h += "self_draw ";
		}
		System.out.println(h);

		return h;
	}

	ScriptEngine engine;

	private ScriptEngine makeEngine() throws ScriptException {
		System.out.println("PythonEngine.makeEngine");
		PySystemState engineSys = new PySystemState();
		System.out.println("Python path:" + engineSys.path.toString());
		Py.setSystemState(engineSys);
		System.out.println("SystemState added");
		ScriptEngine engine = new ScriptEngineManager()
				.getEngineByName("python");
		System.out.println("Got engine");
		engine.eval("from pymcr.majiang import find_mahjong");
		System.out.println("import of mahjong done");
		return engine;
	}

}